#ifndef COMMON_H
#define COMMON_H

#include <cstdint>
#include <type_traits>
#include <variant>

#if __cplusplus >= 202002L
#define HAS_ENTRY(MAP, KEY) (fs->MAP->contains(KEY))
#define HAS_ENTRY2(MAP, KEY) (fs->MAP.contains(KEY))

#define COND_FIELD(COND, T, NAME) \
    [[msvc::no_unique_address]] std::conditional_t<(COND), T, std::monostate> NAME;
#else
#define HAS_ENTRY(MAP, KEY) (fs->MAP->count(KEY))
#define HAS_ENTRY2(MAP, KEY) (fs->MAP.count(KEY))
#endif

enum AllegroVersion
{
    A_160 = 0x00130000,
    A_162 = 0x00130400,
    A_164 = 0x00130C00,
    A_165 = 0x00131000,
    A_166 = 0x00131500,
    A_172 = 0x00140400,
    A_174 = 0x00140900
};

template <typename T, typename = void>
constexpr bool HasVersions = false;

template <typename T>
constexpr bool HasVersions<T, std::void_t<decltype(std::declval<T>().versions)>> = true;

template <AllegroVersion start, AllegroVersion end,
         template <AllegroVersion> class T>
constexpr T<end> upgrade(const T<start> &a)
{
#ifdef _MSC_VER
    if constexpr (HasVersions<T<start>>)
    {
#endif
        constexpr uint8_t n = sizeof(T<start>::versions) / sizeof(AllegroVersion);
        if constexpr (n >= 1 && start < T<start>::versions[0])
        {
            // Reinterpret the element as T<A_160> so we can use the explicitly-
            // defined conversion function.
            return *reinterpret_cast<const T<A_160> *>(&a);
        }
        if constexpr (n >= 2 && start < T<start>::versions[1])
        {
            return *reinterpret_cast<const T<T<start>::versions[0]> *>(&a);
        }
        if constexpr (n >= 3 && start < T<start>::versions[2])
        {
            return *reinterpret_cast<const T<T<start>::versions[1]> *>(&a);
        }
#ifdef _MSC_VER
    }
#endif
    return *reinterpret_cast<const T<end> *>(&a);
}

// This alternative to `sizeof` is used where conditional fields are at the end
// of a `struct`. Without a `uint32_t TAIL` at the end, the size is incorrect.
template <typename T>
constexpr size_t sizeof_until_tail()
{
    return offsetof(T, TAIL);
}

// Linked list
struct ll_ptrs
{
    uint32_t tail;
    uint32_t head;
};

#endif // COMMON_H
